package com.sname.car.bike.service;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.sname.car.bike.dao.BikeMapper;
import com.sname.car.bike.entity.Bike;
import com.sname.car.bike.entity.BikeGen;
import com.sname.car.bike.entity.BikeLocation;
import com.sname.car.common.DateUtil;
import com.sname.car.common.RandomNumberCoder;
import com.sname.car.common.constants.Constants;
import com.sname.car.common.exception.CarException;
import com.sname.car.fee.dao.FeeMapper;
import com.sname.car.fee.entity.Fee;
import com.sname.car.record.dao.RecordMapper;
import com.sname.car.record.entity.Record;
import com.sname.car.user.dao.UserMapper;
import com.sname.car.user.entity.User;
import com.sname.car.user.entity.UserElement;
import com.sname.car.wallet.dao.WalletMapper;
import com.sname.car.wallet.entity.Wallet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;

/**
 * Created By  醉美柳舞之众星捧月
 *
 * @author songx
 * @date 2018/9/25 16:30
 */
@Service
public class BikeServiceImpl implements BikeService {
    private Logger log = LoggerFactory.getLogger(getClass());
    @Autowired
    private FeeMapper feeMapper;
    @Autowired
    private BikeMapper bikeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private WalletMapper walletMapper;
    @Autowired
    private RecordMapper recordMapper;
    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int generateBike() {
        BikeGen bikeGen = new BikeGen();
        /** 我还是没有想通、这一步是干嘛用的 **/
        /** 后来想通了,但是发觉这样做并不好 **/
        /** 但是又想了下, 好像也没得啥子问题..木有更好的解决方法 **/
        bikeMapper.generateBike(bikeGen);
        Bike bike = new Bike();
        bike.setType((byte) 2);
        bike.setNumber(bikeGen.getAutoIncNo());
        int result = bikeMapper.insertSelective(bike);
        return result;
    }

    @Override
    public void unLockBike(UserElement currentUser, Long bikeNo) throws CarException {
        try {
            /** 检验用户是否已经 实名认证、缴纳押金 **/
            User user = userMapper.selectByPrimaryKey(currentUser.getUserId());
            if (user.getVerifyFlag().equals(Constants.NOT_VERYFY)) {
                throw new CarException("用户未认证!");
            }
            /** 检查用户是否有正在进行的骑行记录 **/
            Record recording = recordMapper.selectRecording(currentUser.getUserId());
            if (recording != null) {
                throw new CarException("有正在骑行的记录！");
            }
            /** 检验用户钱包余额是否大于最低余额 **/
            Wallet wallet = walletMapper.selectByUserId(currentUser.getUserId());
            /** 记住字典比较！ 左右大小！ **/
            if (wallet.getRemainSum().compareTo(new BigDecimal(1)) < 0) {
                throw new CarException("余额不足！");
            }
            /** 推送单车,进行解锁！ (貌似不太重要！) **/
            /**
             * JSONObject jsonObject = new JSONObject();
             * jsonObject.put("unlock", "unlock");
             * BaiduPushUtil.pushMsgToSingleDevice(currentUser, "{\"title\":\"TEST\",\"description\":\"Hello Baidu push!\"}");
             */
            /** 修改MongoDB中单车的状态！ **/
            Query query = Query.query(Criteria.where("bike_no").is((bikeNo)));
            Update update = Update.update("status", Constants.BIKE_UNLOCK);
            mongoTemplate.updateFirst(query, update, "bike-position");
            /** 建立骑行订单 记录骑行时间 (记录骑行轨迹 单车上报坐标) **/
            Record record = new Record();
            record.setBikeNo(bikeNo);
            String recordNumberId = System.currentTimeMillis() + RandomNumberCoder.randomNo();
            record.setRecordNo(recordNumberId);
            record.setStartTime(new Date());
            record.setUserid(currentUser.getUserId());
            int i = recordMapper.insertSelective(record);
            if (i <= 0) {
                throw new CarException("创建骑行记录出现异常！");
            }
        } catch (Exception e) {
            log.error("Fail to unlock bike", e);
        }
    }

    @Override
    @Transactional(rollbackFor = CarException.class)
    public void lockBike(BikeLocation bikeLocation) throws CarException {
        try {
            /** 计算骑行时间 **/
            Record record = recordMapper.selectBikeRecordOnGoing(bikeLocation.getBikeNumber());
            if (StringUtils.isEmpty(record)) {
                throw new CarException("骑行记录不存在！");
            }
            /** 查询单车类型 **/
            Bike bike = bikeMapper.selectByBikeNo(bikeLocation.getBikeNumber());
            if (StringUtils.isEmpty(bike)) {
                throw new CarException("单车不存在！");
            }
            /** 查询计价信息  **/
            Fee fee = feeMapper.selectBikeTypeFee(bike.getType());
            if (StringUtils.isEmpty(fee)) {
                throw new CarException("计费信息异常！");
            }
            /** BigDecimal 没有懂这个用法！ 并且注意里面的加减乘除 **/
            BigDecimal cost = BigDecimal.ZERO;
            record.setEndTime(new Date());
            record.setStatus(Constants.RIDE_END);
            Long min = DateUtil.getBetweenMin(new Date(), record.getStartTime());
            record.setRideTime(min.intValue());
            int minUnit = fee.getMinUnit();
            int intMin = min.intValue();
            if (intMin / minUnit == 0) {
                /** 不足一个时间单位,按照一个时间单位算 **/
                cost = fee.getFee();
            } else if (intMin % minUnit == 0) {
                /** 整除了时间单位, 按照倍数直接计费 **/
                cost = fee.getFee().multiply(new BigDecimal(intMin / minUnit));
            } else if (intMin % minUnit != 0) {
                cost = fee.getFee().multiply(new BigDecimal((intMin / minUnit) + 1));
            }
            record.setRideCost(cost);
            recordMapper.updateByPrimaryKeySelective(record);
            /** 钱包扣费 **/
            Long userid = record.getUserid();
            Wallet wallet = walletMapper.selectByUserId(userid);
            /** 下面这一行没有看懂 **/
            wallet.setRemainSum(wallet.getRemainSum().subtract(cost));
            walletMapper.updateByPrimaryKeySelective(wallet);
            /** 修改 MongoDB 中单车的状态 **/
            Query query = Query.query(Criteria.where("bike_no").is(bikeLocation.getBikeNumber()));
            /** 注意下面的写法 ！ **/
            Update update = Update.update("status", Constants.BIKE_LOCK).set("location.coordinates", bikeLocation.getCoordinates());
            mongoTemplate.updateFirst(query, update, "bike-position");
        } catch (Exception e) {
            log.error("fail to lock bike", e);
            throw new CarException("锁定单车失败");
        }
    }

    @Override
    public void reportLocation(BikeLocation bikeLocation) throws CarException {
        /** 查询尚未完成的订单 **/
        Record record = recordMapper.selectBikeRecordOnGoing(bikeLocation.getBikeNumber());
        if (StringUtils.isEmpty(record)) {
            throw new CarException("骑行记录不存在！");
        }
        /** 查询 MongoDB 中是否有骑行的坐标记录数据 **/
        DBObject obj = mongoTemplate.getCollection("ride_contrail").findOne(new BasicDBObject("record_no", record.getRecordNo()));
        if (StringUtils.isEmpty(obj)) {
            ArrayList<BasicDBObject> list = new ArrayList<>();
            /** 暂时没懂 **/
            BasicDBObject temp = new BasicDBObject("loc", bikeLocation.getCoordinates());
            list.add(temp);
            /** 暂时没懂 **/
            BasicDBObject insertObj = new BasicDBObject("record_no", record.getRecordNo()).append("bike_no", record.getBikeNo()).append("contrail", list);
            mongoTemplate.insert(insertObj, "ride_contrail");
        } else {
            /** 暂时没懂 为啥和上面的写法不一样！ **/
            Query query = new Query(Criteria.where("record_no").is(record.getRecordNo()));
            Update update = new Update().push("contrail", new BasicDBObject("loc", bikeLocation.getCoordinates()));
            mongoTemplate.updateFirst(query, update, "ride_contrail");
        }
    }
}
